1. Project Clover database Tue Apr 11 2023 12:41:06 EDT
  2. Package org.joda.time

File DateTimeZone.java

 

Coverage histogram

../../../img/srcFileCovDistChart9.png
50% of files have more coverage

Code metrics

174
353
49
3
1,372
628
160
0.45
7.2
16.33
3.27

Classes

Class Line # Actions
DateTimeZone 115 308 0% 150 52
0.9001919690%
DateTimeZone.Stub 1256 4 0% 4 0
1.0100%
DateTimeZone.LazyInit 1293 41 0% 6 4
0.914893691.5%
 

Contributing tests

This file is covered by 2806 tests. .

Source view

1    /*
2    * Copyright 2001-2014 Stephen Colebourne
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10    * Unless required by applicable law or agreed to in writing, software
11    * distributed under the License is distributed on an "AS IS" BASIS,
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    * See the License for the specific language governing permissions and
14    * limitations under the License.
15    */
16    package org.joda.time;
17   
18    import java.io.File;
19    import java.io.IOException;
20    import java.io.ObjectInputStream;
21    import java.io.ObjectOutputStream;
22    import java.io.ObjectStreamException;
23    import java.io.Serializable;
24    import java.util.Collections;
25    import java.util.HashMap;
26    import java.util.Locale;
27    import java.util.Map;
28    import java.util.Set;
29    import java.util.TimeZone;
30    import java.util.concurrent.atomic.AtomicReference;
31   
32    import org.joda.convert.FromString;
33    import org.joda.convert.ToString;
34    import org.joda.time.chrono.BaseChronology;
35    import org.joda.time.field.FieldUtils;
36    import org.joda.time.format.DateTimeFormatter;
37    import org.joda.time.format.DateTimeFormatterBuilder;
38    import org.joda.time.format.FormatUtils;
39    import org.joda.time.tz.DefaultNameProvider;
40    import org.joda.time.tz.FixedDateTimeZone;
41    import org.joda.time.tz.NameProvider;
42    import org.joda.time.tz.Provider;
43    import org.joda.time.tz.UTCProvider;
44    import org.joda.time.tz.ZoneInfoProvider;
45   
46    /**
47    * DateTimeZone represents a time zone.
48    * <p>
49    * A time zone is a system of rules to convert time from one geographic
50    * location to another. For example, Paris, France is one hour ahead of
51    * London, England. Thus when it is 10:00 in London, it is 11:00 in Paris.
52    * <p>
53    * All time zone rules are expressed, for historical reasons, relative to
54    * Greenwich, London. Local time in Greenwich is referred to as Greenwich Mean
55    * Time (GMT). This is similar, but not precisely identical, to Universal
56    * Coordinated Time, or UTC. This library only uses the term UTC.
57    * <p>
58    * Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00
59    * in the summer. The offset -08:00 indicates that America/Los_Angeles time is
60    * obtained from UTC by adding -08:00, that is, by subtracting 8 hours.
61    * <p>
62    * The offset differs in the summer because of daylight saving time, or DST.
63    * The following definitions are helpful:
64    * <ul>
65    * <li>UTC - The reference time.
66    * <li>Offset - The amount of time a zone differs from UTC. This can vary during the year.
67    * <li>Daylight Saving - The process of having two offsets each year, one in winter and one in summer.
68    * <li>Raw offset - The base offset of the zone.
69    * <li>Additional offset - The additional offset on top of the raw offset.
70    * This is typically zero in winter and one hour in summer in zones that apply DST.
71    * <li>Actual offset - The actual offset that applies, which is the combination of the raw offset and additional offset.
72    * </ul>
73    * <p>
74    * For example, in 2018 Greece applied daylight saving.
75    * Throughout the whole year, the raw offset was +02:00.
76    * In winter, the additional offset was zero, while in summer the additional offset was one hour.
77    * Thus, the actual offset was +02:00 in winter and +03:00 in summer.
78    * <p>
79    * Note: Some governments, most notably Ireland, define daylight saving by describing
80    * a "standard" time in summer and a <i>negative</i> DST offset in winter.
81    * Joda-Time, like the JDK, follows a model for time-zone data where there is a
82    * raw offset all year round and a <i>positive</i> additional offset.
83    * As such, callers cannot assume that the raw offset is that defined by law for the zone.
84    * <p>
85    * Note: Some governments define a daylight saving time that applies for two separate periods.
86    * For example, the year might be winter time, then summer time, then a special time equal
87    * to winter time, then back to summer time before finally dropping back to winter time.
88    * As such, callers cannot assume that the raw and DST offsets directly correlate to summer and winter.
89    * <p>
90    * Unlike the Java TimeZone class, DateTimeZone is immutable. It also only
91    * supports long format time zone ids. Thus PST and ECT are not accepted.
92    * However, the factory that accepts a TimeZone will attempt to convert from
93    * the old short id to a suitable long id.
94    * <p>
95    * There are four approaches to loading time-zone data, which are tried in this order:
96    * <ol>
97    * <li>load the specific {@link Provider} specified by the system property
98    * {@code org.joda.time.DateTimeZone.Provider}.
99    * <li>load {@link ZoneInfoProvider} using the data in the filing system folder
100    * pointed to by system property {@code org.joda.time.DateTimeZone.Folder}.
101    * <li>load {@link ZoneInfoProvider} using the data in the classpath location
102    * {@code org/joda/time/tz/data}.
103    * <li>load {@link UTCProvider}
104    * </ol>
105    * <p>
106    * Unless you override the standard behaviour, the default if the third approach.
107    * <p>
108    * DateTimeZone is thread-safe and immutable, and all subclasses must be as
109    * well.
110    *
111    * @author Brian S O'Neill
112    * @author Stephen Colebourne
113    * @since 1.0
114    */
 
115    public abstract class DateTimeZone implements Serializable {
116   
117    /**
118    * Serialization version.
119    */
120    private static final long serialVersionUID = 5546345482340108586L;
121   
122    /**
123    * The time zone for Universal Coordinated Time
124    */
125    public static final DateTimeZone UTC = UTCDateTimeZone.INSTANCE;
126    /**
127    * Maximum offset.
128    */
129    private static final int MAX_MILLIS = (86400 * 1000) - 1;
130   
131    /**
132    * The instance that is providing time zones.
133    * This is lazily initialized to reduce risks of race conditions at startup.
134    */
135    private static final AtomicReference<Provider> cProvider =
136    new AtomicReference<Provider>();
137    /**
138    * The instance that is providing time zone names.
139    * This is lazily initialized to reduce risks of race conditions at startup.
140    */
141    private static final AtomicReference<NameProvider> cNameProvider =
142    new AtomicReference<NameProvider>();
143    /**
144    * The default time zone.
145    * This is lazily initialized to reduce risks of race conditions at startup.
146    */
147    private static final AtomicReference<DateTimeZone> cDefault =
148    new AtomicReference<DateTimeZone>();
149    /**
150    * The default TZ data path
151    * This is the default classpath location containing the compiled data files.
152    */
153    public static final String DEFAULT_TZ_DATA_PATH = "org/joda/time/tz/data";
154   
155    //-----------------------------------------------------------------------
156   
157    /**
158    * Gets the default time zone.
159    * <p>
160    * The default time zone is derived from the system property {@code user.timezone}.
161    * If that is {@code null} or is not a valid identifier, then the value of the
162    * JDK {@code TimeZone} default is converted. If that fails, {@code UTC} is used.
163    * <p>
164    * NOTE: If the {@code java.util.TimeZone} default is updated <i>after</i> calling this
165    * method, then the change will not be picked up here.
166    *
167    * @return the default datetime zone object
168    */
 
169  18587 toggle public static DateTimeZone getDefault() {
170  18587 DateTimeZone zone = cDefault.get();
171  18587 if (zone == null) {
172  2 try {
173  2 try {
174  2 String id = System.getProperty("user.timezone");
175  2 if (id != null) { // null check avoids stack overflow
176  2 zone = forID(id);
177    }
178    } catch (RuntimeException ex) {
179    // ignored
180    }
181  2 if (zone == null) {
182  2 zone = forTimeZone(TimeZone.getDefault());
183    }
184    } catch (IllegalArgumentException ex) {
185    // ignored
186    }
187  2 if (zone == null) {
188  1 zone = UTC;
189    }
190  2 if (!cDefault.compareAndSet(null, zone)) {
191  0 zone = cDefault.get();
192    }
193    }
194  18587 return zone;
195    }
196   
197    /**
198    * Sets the default time zone.
199    * <p>
200    * NOTE: Calling this method does <i>not</i> set the {@code java.util.TimeZone} default.
201    *
202    * @param zone the default datetime zone object, must not be null
203    * @throws IllegalArgumentException if the zone is null
204    * @throws SecurityException if the application has insufficient security rights
205    */
 
206  6231 toggle public static void setDefault(DateTimeZone zone) throws SecurityException {
207  6231 SecurityManager sm = System.getSecurityManager();
208  6231 if (sm != null) {
209  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setDefault"));
210    }
211  6231 if (zone == null) {
212  1 throw new IllegalArgumentException("The datetime zone must not be null");
213    }
214  6230 cDefault.set(zone);
215    }
216   
217    //-----------------------------------------------------------------------
218   
219    /**
220    * Gets a time zone instance for the specified time zone id.
221    * <p>
222    * The time zone id may be one of those returned by getAvailableIDs.
223    * Short ids, as accepted by {@link java.util.TimeZone}, are not accepted.
224    * All IDs must be specified in the long format.
225    * The exception is UTC, which is an acceptable id.
226    * <p>
227    * Alternatively a locale independent, fixed offset, datetime zone can
228    * be specified. The form <code>[+-]hh:mm</code> can be used.
229    *
230    * @param id the ID of the datetime zone, null means default
231    * @return the DateTimeZone object for the ID
232    * @throws IllegalArgumentException if the ID is not recognised
233    */
 
234  522 toggle @FromString
235    public static DateTimeZone forID(String id) {
236  522 if (id == null) {
237  1 return getDefault();
238    }
239  521 if (id.equals("UTC")) {
240  14 return DateTimeZone.UTC;
241    }
242  507 DateTimeZone zone = getProvider().getZone(id);
243  507 if (zone != null) {
244  468 return zone;
245    }
246  39 if (id.startsWith("+") || id.startsWith("-")) {
247  34 int offset = parseOffset(id);
248  32 if (offset == 0L) {
249  3 return DateTimeZone.UTC;
250    } else {
251  29 id = printOffset(offset);
252  29 return fixedOffsetZone(id, offset);
253    }
254    }
255  5 throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
256    }
257   
258    /**
259    * Gets a time zone instance for the specified offset to UTC in hours.
260    * This method assumes standard length hours.
261    * <p>
262    * This factory is a convenient way of constructing zones with a fixed offset.
263    *
264    * @param hoursOffset the offset in hours from UTC, from -23 to +23
265    * @return the DateTimeZone object for the offset
266    * @throws IllegalArgumentException if the offset is too large or too small
267    */
 
268  76 toggle public static DateTimeZone forOffsetHours(int hoursOffset) throws IllegalArgumentException {
269  76 return forOffsetHoursMinutes(hoursOffset, 0);
270    }
271   
272    /**
273    * Gets a time zone instance for the specified offset to UTC in hours and minutes.
274    * This method assumes 60 minutes in an hour, and standard length minutes.
275    * <p>
276    * This factory is a convenient way of constructing zones with a fixed offset.
277    * The hours value must be in the range -23 to +23.
278    * The minutes value must be in the range -59 to +59.
279    * The following combinations of sign for the hour and minute are possible:
280    * <pre>
281    * Hour Minute Example Result
282    *
283    * +ve +ve (2, 15) +02:15
284    * +ve zero (2, 0) +02:00
285    * +ve -ve (2, -15) IllegalArgumentException
286    *
287    * zero +ve (0, 15) +00:15
288    * zero zero (0, 0) +00:00
289    * zero -ve (0, -15) -00:15
290    *
291    * -ve +ve (-2, 15) -02:15
292    * -ve zero (-2, 0) -02:00
293    * -ve -ve (-2, -15) -02:15
294    * </pre>
295    * Note that in versions before 2.3, the minutes had to be zero or positive.
296    *
297    * @param hoursOffset the offset in hours from UTC, from -23 to +23
298    * @param minutesOffset the offset in minutes from UTC, from -59 to +59
299    * @return the DateTimeZone object for the offset
300    * @throws IllegalArgumentException if any value is out of range, the minutes are negative
301    * when the hours are positive, or the resulting offset exceeds +/- 23:59:59.000
302    */
 
303  93 toggle public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
304  93 if (hoursOffset == 0 && minutesOffset == 0) {
305  4 return DateTimeZone.UTC;
306    }
307  89 if (hoursOffset < -23 || hoursOffset > 23) {
308  3 throw new IllegalArgumentException("Hours out of range: " + hoursOffset);
309    }
310  86 if (minutesOffset < -59 || minutesOffset > 59) {
311  2 throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
312    }
313  84 if (hoursOffset > 0 && minutesOffset < 0) {
314  1 throw new IllegalArgumentException("Positive hours must not have negative minutes: " + minutesOffset);
315    }
316  83 int offset = 0;
317  83 try {
318  83 int hoursInMinutes = hoursOffset * 60;
319  83 if (hoursInMinutes < 0) {
320  32 minutesOffset = hoursInMinutes - Math.abs(minutesOffset);
321    } else {
322  51 minutesOffset = hoursInMinutes + minutesOffset;
323    }
324  83 offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
325    } catch (ArithmeticException ex) {
326  0 throw new IllegalArgumentException("Offset is too large");
327    }
328  83 return forOffsetMillis(offset);
329    }
330   
331    /**
332    * Gets a time zone instance for the specified offset to UTC in milliseconds.
333    *
334    * @param millisOffset the offset in millis from UTC, from -23:59:59.999 to +23:59:59.999
335    * @return the DateTimeZone object for the offset
336    */
 
337  177 toggle public static DateTimeZone forOffsetMillis(int millisOffset) {
338  177 if (millisOffset < -MAX_MILLIS || millisOffset > MAX_MILLIS) {
339  0 throw new IllegalArgumentException("Millis out of range: " + millisOffset);
340    }
341  177 String id = printOffset(millisOffset);
342  177 return fixedOffsetZone(id, millisOffset);
343    }
344   
345    /**
346    * Gets a time zone instance for a JDK TimeZone.
347    * <p>
348    * DateTimeZone only accepts a subset of the IDs from TimeZone. The
349    * excluded IDs are the short three letter form (except UTC). This
350    * method will attempt to convert between time zones created using the
351    * short IDs and the full version.
352    * <p>
353    * This method is not designed to parse time zones with rules created by
354    * applications using <code>SimpleTimeZone</code> directly.
355    *
356    * @param zone the zone to convert, null means default
357    * @return the DateTimeZone object for the zone
358    * @throws IllegalArgumentException if the zone is not recognised
359    */
 
360  72 toggle public static DateTimeZone forTimeZone(TimeZone zone) {
361  72 if (zone == null) {
362  1 return getDefault();
363    }
364  71 final String id = zone.getID();
365  71 if (id == null) {
366  1 throw new IllegalArgumentException("The TimeZone id must not be null");
367    }
368  70 if (id.equals("UTC")) {
369  1 return DateTimeZone.UTC;
370    }
371   
372    // Convert from old alias before consulting provider since they may differ.
373  69 DateTimeZone dtz = null;
374  69 String convId = getConvertedId(id);
375  69 Provider provider = getProvider();
376  69 if (convId != null) {
377  35 dtz = provider.getZone(convId);
378    }
379  69 if (dtz == null) {
380  34 dtz = provider.getZone(id);
381    }
382  69 if (dtz != null) {
383  58 return dtz;
384    }
385   
386    // Support GMT+/-hh:mm formats
387  11 if (convId == null) {
388  11 convId = id;
389  11 if (convId.startsWith("GMT+") || convId.startsWith("GMT-")) {
390  9 convId = convId.substring(3);
391  9 if (convId.length() > 2) {
392  9 char firstDigit = convId.charAt(1);
393  9 if (firstDigit > '9' && Character.isDigit(firstDigit)) {
394  1 convId = convertToAsciiNumber(convId);
395    }
396    }
397  9 int offset = parseOffset(convId);
398  9 if (offset == 0L) {
399  3 return DateTimeZone.UTC;
400    } else {
401  6 convId = printOffset(offset);
402  6 return fixedOffsetZone(convId, offset);
403    }
404    }
405    }
406  2 throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
407    }
408   
 
409  1 toggle private static String convertToAsciiNumber(String convId) {
410  1 StringBuilder buf = new StringBuilder(convId);
411  7 for (int i = 0; i < buf.length(); i++) {
412  6 char ch = buf.charAt(i);
413  6 int digit = Character.digit(ch, 10);
414  6 if (digit >= 0) {
415  4 buf.setCharAt(i, (char) ('0' + digit));
416    }
417    }
418  1 return buf.toString();
419    }
420   
421    //-----------------------------------------------------------------------
422   
423    /**
424    * Gets the zone using a fixed offset amount.
425    *
426    * @param id the zone id
427    * @param offset the offset in millis
428    * @return the zone
429    */
 
430  212 toggle private static DateTimeZone fixedOffsetZone(String id, int offset) {
431  212 if (offset == 0) {
432  17 return DateTimeZone.UTC;
433    }
434  195 return new FixedDateTimeZone(id, null, offset, offset);
435    }
436   
437    /**
438    * Gets all the available IDs supported.
439    *
440    * @return an unmodifiable Set of String IDs
441    */
 
442  10 toggle public static Set<String> getAvailableIDs() {
443  10 return getProvider().getAvailableIDs();
444    }
445   
446    //-----------------------------------------------------------------------
447   
448    /**
449    * Gets the zone provider factory.
450    * <p>
451    * The zone provider is a pluggable instance factory that supplies the
452    * actual instances of DateTimeZone.
453    *
454    * @return the provider
455    */
 
456  596 toggle public static Provider getProvider() {
457  596 Provider provider = cProvider.get();
458  596 if (provider == null) {
459  2 provider = getDefaultProvider();
460  2 if (!cProvider.compareAndSet(null, provider)) {
461  0 provider = cProvider.get();
462    }
463    }
464  596 return provider;
465    }
466   
467    /**
468    * Sets the zone provider factory.
469    * <p>
470    * The zone provider is a pluggable instance factory that supplies the
471    * actual instances of DateTimeZone.
472    *
473    * @param provider provider to use, or null for default
474    * @throws SecurityException if you do not have the permission DateTimeZone.setProvider
475    * @throws IllegalArgumentException if the provider is invalid
476    */
 
477  15 toggle public static void setProvider(Provider provider) throws SecurityException {
478  15 SecurityManager sm = System.getSecurityManager();
479  15 if (sm != null) {
480  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setProvider"));
481    }
482  15 if (provider == null) {
483  10 provider = getDefaultProvider();
484    } else {
485  5 validateProvider(provider);
486    }
487  10 cProvider.set(provider);
488    }
489   
490    /**
491    * Sets the zone provider factory without performing the security check.
492    *
493    * @param provider provider to use, or null for default
494    * @return the provider
495    * @throws IllegalArgumentException if the provider is invalid
496    */
 
497  16 toggle private static Provider validateProvider(Provider provider) {
498  16 Set<String> ids = provider.getAvailableIDs();
499  16 if (ids == null || ids.size() == 0) {
500  2 throw new IllegalArgumentException("The provider doesn't have any available ids");
501    }
502  14 if (!ids.contains("UTC")) {
503  1 throw new IllegalArgumentException("The provider doesn't support UTC");
504    }
505  13 if (!UTC.equals(provider.getZone("UTC"))) {
506  1 throw new IllegalArgumentException("Invalid UTC zone provided");
507    }
508  12 return provider;
509    }
510   
511    /**
512    * Gets the default zone provider.
513    * <p>
514    * This tries four approaches to loading data:
515    * <ol>
516    * <li>loads the provider identifier by the system property
517    * <code>org.joda.time.DateTimeZone.Provider</code>.
518    * <li>load <code>ZoneInfoProvider</code> using the data in the filing system folder
519    * pointed to by system property <code>org.joda.time.DateTimeZone.Folder</code>.
520    * <li>loads <code>ZoneInfoProvider</code> using the data in the classpath location
521    * <code>org/joda/time/tz/data</code>.
522    * <li>loads <code>UTCProvider</code>.
523    * </ol>
524    * <p>
525    * Unless you override the standard behaviour, the default if the third approach.
526    *
527    * @return the default name provider
528    */
 
529  12 toggle private static Provider getDefaultProvider() {
530    // approach 1
531  12 try {
532  12 String providerClass = System.getProperty("org.joda.time.DateTimeZone.Provider");
533  12 if (providerClass != null) {
534  3 try {
535    // do not initialize the class until the type has been checked
536  3 Class<?> cls = Class.forName(providerClass, false, DateTimeZone.class.getClassLoader());
537  2 if (!Provider.class.isAssignableFrom(cls)) {
538  0 throw new IllegalArgumentException("System property referred to class that does not implement " + Provider.class);
539    }
540  2 Provider provider = cls.asSubclass(Provider.class).getConstructor().newInstance();
541  2 return validateProvider(provider);
542    } catch (Exception ex) {
543  1 throw new RuntimeException(ex);
544    }
545    }
546    } catch (SecurityException ex) {
547    // ignored
548    }
549    // approach 2
550  9 try {
551  9 String dataFolder = System.getProperty("org.joda.time.DateTimeZone.Folder");
552  9 if (dataFolder != null) {
553  1 try {
554  1 Provider provider = new ZoneInfoProvider(new File(dataFolder));
555  1 return validateProvider(provider);
556    } catch (Exception ex) {
557  0 throw new RuntimeException(ex);
558    }
559    }
560    } catch (SecurityException ex) {
561    // ignored
562    }
563    // approach 3
564  8 try {
565  8 Provider provider = new ZoneInfoProvider(DEFAULT_TZ_DATA_PATH);
566  8 return validateProvider(provider);
567    } catch (Exception ex) {
568  0 ex.printStackTrace();
569    }
570    // approach 4
571  0 return new UTCProvider();
572    }
573   
574    //-----------------------------------------------------------------------
575   
576    /**
577    * Gets the name provider factory.
578    * <p>
579    * The name provider is a pluggable instance factory that supplies the
580    * names of each DateTimeZone.
581    *
582    * @return the provider
583    */
 
584  16 toggle public static NameProvider getNameProvider() {
585  16 NameProvider nameProvider = cNameProvider.get();
586  16 if (nameProvider == null) {
587  1 nameProvider = getDefaultNameProvider();
588  1 if (!cNameProvider.compareAndSet(null, nameProvider)) {
589  0 nameProvider = cNameProvider.get();
590    }
591    }
592  16 return nameProvider;
593    }
594   
595    /**
596    * Sets the name provider factory.
597    * <p>
598    * The name provider is a pluggable instance factory that supplies the
599    * names of each DateTimeZone.
600    *
601    * @param nameProvider provider to use, or null for default
602    * @throws SecurityException if you do not have the permission DateTimeZone.setNameProvider
603    * @throws IllegalArgumentException if the provider is invalid
604    */
 
605  5 toggle public static void setNameProvider(NameProvider nameProvider) throws SecurityException {
606  5 SecurityManager sm = System.getSecurityManager();
607  5 if (sm != null) {
608  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setNameProvider"));
609    }
610  5 if (nameProvider == null) {
611  4 nameProvider = getDefaultNameProvider();
612    }
613  5 cNameProvider.set(nameProvider);
614    }
615   
616    /**
617    * Gets the default name provider.
618    * <p>
619    * Tries the system property <code>org.joda.time.DateTimeZone.NameProvider</code>.
620    * Then uses <code>DefaultNameProvider</code>.
621    *
622    * @return the default name provider
623    */
 
624  5 toggle private static NameProvider getDefaultNameProvider() {
625  5 NameProvider nameProvider = null;
626  5 try {
627  5 String providerClass = System.getProperty("org.joda.time.DateTimeZone.NameProvider");
628  5 if (providerClass != null) {
629  1 try {
630    // do not initialize the class until the type has been checked
631  1 Class<?> cls = Class.forName(providerClass, false, DateTimeZone.class.getClassLoader());
632  1 if (!NameProvider.class.isAssignableFrom(cls)) {
633  0 throw new IllegalArgumentException("System property referred to class that does not implement " + NameProvider.class);
634    }
635  1 nameProvider = cls.asSubclass(NameProvider.class).getConstructor().newInstance();
636    } catch (Exception ex) {
637  0 throw new RuntimeException(ex);
638    }
639    }
640    } catch (SecurityException ex) {
641    // ignore
642    }
643   
644  5 if (nameProvider == null) {
645  4 nameProvider = new DefaultNameProvider();
646    }
647   
648  5 return nameProvider;
649    }
650   
651    //-----------------------------------------------------------------------
652   
653    /**
654    * Converts an old style id to a new style id.
655    *
656    * @param id the old style id
657    * @return the new style id, null if not found
658    */
 
659  69 toggle private static String getConvertedId(String id) {
660  69 return LazyInit.CONVERSION_MAP.get(id);
661    }
662   
663    /**
664    * Parses an offset from the string.
665    *
666    * @param str the string
667    * @return the offset millis
668    */
 
669  43 toggle private static int parseOffset(String str) {
670  43 return -(int) LazyInit.OFFSET_FORMATTER.parseMillis(str);
671    }
672   
673    /**
674    * Formats a timezone offset string.
675    * <p>
676    * This method is kept separate from the formatting classes to speed and
677    * simplify startup and classloading.
678    *
679    * @param offset the offset in milliseconds
680    * @return the time zone string
681    */
 
682  214 toggle private static String printOffset(int offset) {
683  214 StringBuffer buf = new StringBuffer();
684  214 if (offset >= 0) {
685  160 buf.append('+');
686    } else {
687  54 buf.append('-');
688  54 offset = -offset;
689    }
690   
691  214 int hours = offset / DateTimeConstants.MILLIS_PER_HOUR;
692  214 FormatUtils.appendPaddedInteger(buf, hours, 2);
693  214 offset -= hours * (int) DateTimeConstants.MILLIS_PER_HOUR;
694   
695  214 int minutes = offset / DateTimeConstants.MILLIS_PER_MINUTE;
696  214 buf.append(':');
697  214 FormatUtils.appendPaddedInteger(buf, minutes, 2);
698  214 offset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
699  214 if (offset == 0) {
700  205 return buf.toString();
701    }
702   
703  9 int seconds = offset / DateTimeConstants.MILLIS_PER_SECOND;
704  9 buf.append(':');
705  9 FormatUtils.appendPaddedInteger(buf, seconds, 2);
706  9 offset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
707  9 if (offset == 0) {
708  2 return buf.toString();
709    }
710   
711  7 buf.append('.');
712  7 FormatUtils.appendPaddedInteger(buf, offset, 3);
713  7 return buf.toString();
714    }
715   
716    // Instance fields and methods
717    //--------------------------------------------------------------------
718   
719    private final String iID;
720   
721    /**
722    * Constructor.
723    *
724    * @param id the id to use
725    * @throws IllegalArgumentException if the id is null
726    */
 
727  3203 toggle protected DateTimeZone(String id) {
728  3203 if (id == null) {
729  1 throw new IllegalArgumentException("Id must not be null");
730    }
731  3202 iID = id;
732    }
733   
734    // Principal methods
735    //--------------------------------------------------------------------
736   
737    /**
738    * Gets the ID of this datetime zone.
739    *
740    * @return the ID of this datetime zone
741    */
 
742  117873 toggle @ToString
743    public final String getID() {
744  117873 return iID;
745    }
746   
747    /**
748    * Returns a non-localized name that is unique to this time zone. It can be
749    * combined with id to form a unique key for fetching localized names.
750    *
751    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
752    * @return name key or null if id should be used for names
753    */
754    public abstract String getNameKey(long instant);
755   
756    /**
757    * Gets the short name of this datetime zone suitable for display using
758    * the default locale.
759    * <p>
760    * If the name is not available for the locale, then this method returns a
761    * string in the format <code>[+-]hh:mm</code>.
762    *
763    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
764    * @return the human-readable short name in the default locale
765    */
 
766  1 toggle public final String getShortName(long instant) {
767  1 return getShortName(instant, null);
768    }
769   
770    /**
771    * Gets the short name of this datetime zone suitable for display using
772    * the specified locale.
773    * <p>
774    * If the name is not available for the locale, then this method returns a
775    * string in the format <code>[+-]hh:mm</code>.
776    *
777    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
778    * @param locale the locale to get the name for
779    * @return the human-readable short name in the specified locale
780    */
 
781  2 toggle public String getShortName(long instant, Locale locale) {
782  2 if (locale == null) {
783  1 locale = Locale.getDefault();
784    }
785  2 String nameKey = getNameKey(instant);
786  2 if (nameKey == null) {
787  1 return iID;
788    }
789  1 String name;
790  1 NameProvider np = getNameProvider();
791  1 if (np instanceof DefaultNameProvider) {
792  0 name = ((DefaultNameProvider) np).getShortName(locale, iID, nameKey, isStandardOffset(instant));
793    } else {
794  1 name = np.getShortName(locale, iID, nameKey);
795    }
796  1 if (name != null) {
797  0 return name;
798    }
799  1 return printOffset(getOffset(instant));
800    }
801   
802    /**
803    * Gets the long name of this datetime zone suitable for display using
804    * the default locale.
805    * <p>
806    * If the name is not available for the locale, then this method returns a
807    * string in the format <code>[+-]hh:mm</code>.
808    *
809    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
810    * @return the human-readable long name in the default locale
811    */
 
812  1 toggle public final String getName(long instant) {
813  1 return getName(instant, null);
814    }
815   
816    /**
817    * Gets the long name of this datetime zone suitable for display using
818    * the specified locale.
819    * <p>
820    * If the name is not available for the locale, then this method returns a
821    * string in the format <code>[+-]hh:mm</code>.
822    *
823    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
824    * @param locale the locale to get the name for
825    * @return the human-readable long name in the specified locale
826    */
 
827  2 toggle public String getName(long instant, Locale locale) {
828  2 if (locale == null) {
829  1 locale = Locale.getDefault();
830    }
831  2 String nameKey = getNameKey(instant);
832  2 if (nameKey == null) {
833  1 return iID;
834    }
835  1 String name;
836  1 NameProvider np = getNameProvider();
837  1 if (np instanceof DefaultNameProvider) {
838  0 name = ((DefaultNameProvider) np).getName(locale, iID, nameKey, isStandardOffset(instant));
839    } else {
840  1 name = np.getName(locale, iID, nameKey);
841    }
842  1 if (name != null) {
843  0 return name;
844    }
845  1 return printOffset(getOffset(instant));
846    }
847   
848    /**
849    * Gets the millisecond offset to add to UTC to get local time.
850    * <p>
851    * This returns the actual offset from UTC for the zone at the specified instant.
852    * If the method is called with a different instant, the offset returned may be different
853    * as a result of daylight saving or other government rule changes.
854    *
855    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
856    * @return the millisecond offset to add to UTC to get local time
857    */
858    public abstract int getOffset(long instant);
859   
860    /**
861    * Gets the millisecond offset to add to UTC to get local time.
862    * <p>
863    * This returns the actual offset from UTC for the zone at the specified instant.
864    * If the method is called with a different instant, the offset returned may be different
865    * as a result of daylight saving or other government rule changes.
866    *
867    * @param instant instant to get the offset for, null means now
868    * @return the millisecond offset to add to UTC to get local time
869    */
 
870  6 toggle public final int getOffset(ReadableInstant instant) {
871  6 if (instant == null) {
872  2 return getOffset(DateTimeUtils.currentTimeMillis());
873    }
874  4 return getOffset(instant.getMillis());
875    }
876   
877    /**
878    * Gets the raw millisecond offset to add to UTC.
879    * <p>
880    * This should be treated as an implementation detail.
881    * End-users should use {@link #getOffset(long)}.
882    * <p>
883    * This returns the raw offset from UTC for the zone at the specified instant, effectively ignoring DST.
884    * If the method is called with a different instant, the offset returned may be different
885    * as a result of government rule changes.
886    * <p>
887    * This method should be named {@code getRawOffset()} but cannot be renamed for compatibility reasons.
888    *
889    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
890    * @return the millisecond offset to add to UTC to get local time
891    */
892    public abstract int getStandardOffset(long instant);
893   
894    /**
895    * Checks whether, at a particular instant, the offset is raw or not.
896    * <p>
897    * This method can be used to estimate whether Summer Time (DST) applies at the specified instant.
898    * As a general rule, if the actual offset equals the raw offset at the specified instant
899    * then either winter time applies or the zone does not have DST rules.
900    * If the actual offset does not equal the raw offset, then some form of Summer Time applies.
901    * <p>
902    * The implementation of the method is simply whether {@link #getOffset(long)}
903    * equals {@link #getStandardOffset(long)} at the specified instant.
904    * <p>
905    * This method should be named {@code isRawOffsetInUse()} but cannot be renamed for compatibility reasons.
906    *
907    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
908    * @return true if the offset at the given instant is the same as the raw offset
909    * @since 1.5
910    */
 
911  13 toggle public boolean isStandardOffset(long instant) {
912  13 return getOffset(instant) == getStandardOffset(instant);
913    }
914   
915    /**
916    * Gets the millisecond offset to subtract from local time to get UTC time.
917    * This offset can be used to undo adding the offset obtained by getOffset.
918    *
919    * <pre>
920    * millisLocal == millisUTC + getOffset(millisUTC)
921    * millisUTC == millisLocal - getOffsetFromLocal(millisLocal)
922    * </pre>
923    * <p>
924    * NOTE: After calculating millisLocal, some error may be introduced. At
925    * offset transitions (due to DST or other historical changes), ranges of
926    * local times may map to different UTC times.
927    * <p>
928    * For overlaps (where the local time is ambiguous), this method returns the
929    * offset applicable before the gap. The effect of this is that any instant
930    * calculated using the offset from an overlap will be in "summer" time.
931    * <p>
932    * For gaps, this method returns the offset applicable before the gap, ie "winter" offset.
933    * However, the effect of this is that any instant calculated using the offset
934    * from a gap will be after the gap, in "summer" time.
935    * <p>
936    * For example, consider a zone with a gap from 01:00 to 01:59:<br />
937    * Input: 00:00 (before gap) Output: Offset applicable before gap DateTime: 00:00<br />
938    * Input: 00:30 (before gap) Output: Offset applicable before gap DateTime: 00:30<br />
939    * Input: 01:00 (in gap) Output: Offset applicable before gap DateTime: 02:00<br />
940    * Input: 01:30 (in gap) Output: Offset applicable before gap DateTime: 02:30<br />
941    * Input: 02:00 (after gap) Output: Offset applicable after gap DateTime: 02:00<br />
942    * Input: 02:30 (after gap) Output: Offset applicable after gap DateTime: 02:30<br />
943    * <p>
944    * NOTE: Prior to v2.0, the DST overlap behaviour was not defined and varied by hemisphere.
945    * Prior to v1.5, the DST gap behaviour was also not defined.
946    * In v2.4, the documentation was clarified again.
947    *
948    * @param instantLocal the millisecond instant, relative to this time zone, to get the offset for
949    * @return the millisecond offset to subtract from local time to get UTC time
950    */
 
951  16710 toggle public int getOffsetFromLocal(long instantLocal) {
952    // get the offset at instantLocal (first estimate)
953  16710 final int offsetLocal = getOffset(instantLocal);
954    // adjust instantLocal using the estimate and recalc the offset
955  16710 final long instantAdjusted = instantLocal - offsetLocal;
956  16710 final int offsetAdjusted = getOffset(instantAdjusted);
957    // if the offsets differ, we must be near a DST boundary
958  16710 if (offsetLocal != offsetAdjusted) {
959    // we need to ensure that time is always after the DST gap
960    // this happens naturally for positive offsets, but not for negative
961  92 if ((offsetLocal - offsetAdjusted) < 0) {
962    // if we just return offsetAdjusted then the time is pushed
963    // back before the transition, whereas it should be
964    // on or after the transition
965  49 long nextLocal = nextTransition(instantAdjusted);
966  49 if (nextLocal == (instantLocal - offsetLocal)) {
967  0 nextLocal = Long.MAX_VALUE;
968    }
969  49 long nextAdjusted = nextTransition(instantLocal - offsetAdjusted);
970  49 if (nextAdjusted == (instantLocal - offsetAdjusted)) {
971  0 nextAdjusted = Long.MAX_VALUE;
972    }
973  49 if (nextLocal != nextAdjusted) {
974  17 return offsetLocal;
975    }
976    }
977  16618 } else if (offsetLocal >= 0) {
978  1754 long prev = previousTransition(instantAdjusted);
979  1754 if (prev < instantAdjusted) {
980  1754 int offsetPrev = getOffset(prev);
981  1754 int diff = offsetPrev - offsetLocal;
982  1754 if (instantAdjusted - prev <= diff) {
983  104 return offsetPrev;
984    }
985    }
986    }
987  16589 return offsetAdjusted;
988    }
989   
990    /**
991    * Converts an actual UTC instant to a local instant with the same
992    * local time. This conversion is used before performing a calculation
993    * so that the calculation can be done using a simple local zone.
994    *
995    * @param instantUTC the UTC instant to convert to local
996    * @return the local instant with the same local time
997    * @throws ArithmeticException if the result overflows a long
998    * @since 1.5
999    */
 
1000  154644 toggle public long convertUTCToLocal(long instantUTC) {
1001  154644 int offset = getOffset(instantUTC);
1002  154644 long instantLocal = instantUTC + offset;
1003    // If there is a sign change, but the two values have the same sign...
1004  154644 if ((instantUTC ^ instantLocal) < 0 && (instantUTC ^ offset) >= 0) {
1005  0 throw new ArithmeticException("Adding time zone offset caused overflow");
1006    }
1007  154644 return instantLocal;
1008    }
1009   
1010    /**
1011    * Converts a local instant to an actual UTC instant with the same
1012    * local time attempting to use the same offset as the original.
1013    * <p>
1014    * This conversion is used after performing a calculation
1015    * where the calculation was done using a simple local zone.
1016    * Whenever possible, the same offset as the original offset will be used.
1017    * This is most significant during a daylight savings overlap.
1018    *
1019    * @param instantLocal the local instant to convert to UTC
1020    * @param strict whether the conversion should reject non-existent local times
1021    * @param originalInstantUTC the original instant that the calculation is based on
1022    * @return the UTC instant with the same local time,
1023    * @throws ArithmeticException if the result overflows a long
1024    * @throws IllegalArgumentException if the zone has no equivalent local time
1025    * @since 2.0
1026    */
 
1027  45855 toggle public long convertLocalToUTC(long instantLocal, boolean strict, long originalInstantUTC) {
1028  45855 int offsetOriginal = getOffset(originalInstantUTC);
1029  45855 long instantUTC = instantLocal - offsetOriginal;
1030  45855 int offsetLocalFromOriginal = getOffset(instantUTC);
1031  45855 if (offsetLocalFromOriginal == offsetOriginal) {
1032  45598 return instantUTC;
1033    }
1034  257 return convertLocalToUTC(instantLocal, strict);
1035    }
1036   
1037    /**
1038    * Converts a local instant to an actual UTC instant with the same
1039    * local time. This conversion is used after performing a calculation
1040    * where the calculation was done using a simple local zone.
1041    *
1042    * @param instantLocal the local instant to convert to UTC
1043    * @param strict whether the conversion should reject non-existent local times
1044    * @return the UTC instant with the same local time,
1045    * @throws ArithmeticException if the result overflows a long
1046    * @throws IllegalInstantException if the zone has no equivalent local time
1047    * @since 1.5
1048    */
 
1049  282 toggle public long convertLocalToUTC(long instantLocal, boolean strict) {
1050    // get the offset at instantLocal (first estimate)
1051  282 int offsetLocal = getOffset(instantLocal);
1052    // adjust instantLocal using the estimate and recalc the offset
1053  282 int offset = getOffset(instantLocal - offsetLocal);
1054    // if the offsets differ, we must be near a DST boundary
1055  282 if (offsetLocal != offset) {
1056    // if strict then always check if in DST gap
1057    // otherwise only check if zone in Western hemisphere (as the
1058    // value of offset is already correct for Eastern hemisphere)
1059  34 if (strict || offsetLocal < 0) {
1060    // determine if we are in the DST gap
1061  14 long nextLocal = nextTransition(instantLocal - offsetLocal);
1062  14 if (nextLocal == (instantLocal - offsetLocal)) {
1063  0 nextLocal = Long.MAX_VALUE;
1064    }
1065  14 long nextAdjusted = nextTransition(instantLocal - offset);
1066  14 if (nextAdjusted == (instantLocal - offset)) {
1067  0 nextAdjusted = Long.MAX_VALUE;
1068    }
1069  14 if (nextLocal != nextAdjusted) {
1070    // yes we are in the DST gap
1071  13 if (strict) {
1072    // DST gap is not acceptable
1073  0 throw new IllegalInstantException(instantLocal, getID());
1074    } else {
1075    // DST gap is acceptable, but for the Western hemisphere
1076    // the offset is wrong and will result in local times
1077    // before the cutover so use the offsetLocal instead
1078  13 offset = offsetLocal;
1079    }
1080    }
1081    }
1082    }
1083    // check for overflow
1084  282 long instantUTC = instantLocal - offset;
1085    // If there is a sign change, but the two values have different signs...
1086  282 if ((instantLocal ^ instantUTC) < 0 && (instantLocal ^ offset) < 0) {
1087  0 throw new ArithmeticException("Subtracting time zone offset caused overflow");
1088    }
1089  282 return instantUTC;
1090    }
1091   
1092    /**
1093    * Gets the millisecond instant in another zone keeping the same local time.
1094    * <p>
1095    * The conversion is performed by converting the specified UTC millis to local
1096    * millis in this zone, then converting back to UTC millis in the new zone.
1097    *
1098    * @param newZone the new zone, null means default
1099    * @param oldInstant the UTC millisecond instant to convert
1100    * @return the UTC millisecond instant with the same local time in the new zone
1101    */
 
1102  30791 toggle public long getMillisKeepLocal(DateTimeZone newZone, long oldInstant) {
1103  30791 if (newZone == null) {
1104  1 newZone = DateTimeZone.getDefault();
1105    }
1106  30791 if (newZone == this) {
1107  30537 return oldInstant;
1108    }
1109  254 long instantLocal = convertUTCToLocal(oldInstant);
1110  254 return newZone.convertLocalToUTC(instantLocal, false, oldInstant);
1111    }
1112   
1113    /**
1114    * Checks if the given {@link LocalDateTime} is within a gap.
1115    * <p>
1116    * When switching into Daylight Savings Time there is typically a gap where a clock hour is missing.
1117    * This method identifies whether the local datetime refers to such a gap.
1118    *
1119    * @param localDateTime the time to check, not null
1120    * @return true if the given datetime refers to a gap
1121    * @since 1.6
1122    */
 
1123  22 toggle public boolean isLocalDateTimeGap(LocalDateTime localDateTime) {
1124  22 if (isFixed()) {
1125  0 return false;
1126    }
1127  22 try {
1128  22 localDateTime.toDateTime(this);
1129  16 return false;
1130    } catch (IllegalInstantException ex) {
1131  6 return true;
1132    }
1133    }
1134   
1135    /**
1136    * Adjusts the offset to be the earlier or later one during an overlap.
1137    *
1138    * @param instant the instant to adjust
1139    * @param earlierOrLater false for earlier, true for later
1140    * @return the adjusted instant millis
1141    */
 
1142  51 toggle public long adjustOffset(long instant, boolean earlierOrLater) {
1143    // a bit messy, but will work in all non-pathological cases
1144   
1145    // evaluate 3 hours before and after to work out if anything is happening
1146  51 long instantBefore = instant - 3 * DateTimeConstants.MILLIS_PER_HOUR;
1147  51 long instantAfter = instant + 3 * DateTimeConstants.MILLIS_PER_HOUR;
1148  51 long offsetBefore = getOffset(instantBefore);
1149  51 long offsetAfter = getOffset(instantAfter);
1150  51 if (offsetBefore <= offsetAfter) {
1151  29 return instant; // not an overlap (less than is a gap, equal is normal case)
1152    }
1153   
1154    // work out range of instants that have duplicate local times
1155  22 long diff = offsetBefore - offsetAfter;
1156  22 long transition = nextTransition(instantBefore);
1157  22 long overlapStart = transition - diff;
1158  22 long overlapEnd = transition + diff;
1159  22 if (instant < overlapStart || instant >= overlapEnd) {
1160  4 return instant; // not an overlap
1161    }
1162   
1163    // calculate result
1164  18 long afterStart = instant - overlapStart;
1165  18 if (afterStart >= diff) {
1166    // currently in later offset
1167  6 return earlierOrLater ? instant : instant - diff;
1168    } else {
1169    // currently in earlier offset
1170  12 return earlierOrLater ? instant + diff : instant;
1171    }
1172    }
1173    // System.out.println(new DateTime(transitionStart, DateTimeZone.UTC) + " " + new DateTime(transitionStart, this));
1174   
1175    //-----------------------------------------------------------------------
1176   
1177    /**
1178    * Returns true if this time zone has no transitions.
1179    *
1180    * @return true if no transitions
1181    */
1182    public abstract boolean isFixed();
1183   
1184    /**
1185    * Advances the given instant to where the time zone offset or name changes.
1186    * If the instant returned is exactly the same as passed in, then
1187    * no changes occur after the given instant.
1188    *
1189    * @param instant milliseconds from 1970-01-01T00:00:00Z
1190    * @return milliseconds from 1970-01-01T00:00:00Z
1191    */
1192    public abstract long nextTransition(long instant);
1193   
1194    /**
1195    * Retreats the given instant to where the time zone offset or name changes.
1196    * If the instant returned is exactly the same as passed in, then
1197    * no changes occur before the given instant.
1198    *
1199    * @param instant milliseconds from 1970-01-01T00:00:00Z
1200    * @return milliseconds from 1970-01-01T00:00:00Z
1201    */
1202    public abstract long previousTransition(long instant);
1203   
1204    // Basic methods
1205    //--------------------------------------------------------------------
1206   
1207    /**
1208    * Get the datetime zone as a {@link java.util.TimeZone}.
1209    *
1210    * @return the closest matching TimeZone object
1211    */
 
1212  637 toggle public java.util.TimeZone toTimeZone() {
1213  637 return java.util.TimeZone.getTimeZone(iID);
1214    }
1215   
1216    /**
1217    * Compare this datetime zone with another.
1218    *
1219    * @param object the object to compare with
1220    * @return true if equal, based on the ID and all internal rules
1221    */
1222    public abstract boolean equals(Object object);
1223   
1224    /**
1225    * Gets a hash code compatible with equals.
1226    *
1227    * @return suitable hashcode
1228    */
 
1229  17829 toggle public int hashCode() {
1230  17829 return 57 + getID().hashCode();
1231    }
1232   
1233    /**
1234    * Gets the datetime zone as a string, which is simply its ID.
1235    *
1236    * @return the id of the zone
1237    */
 
1238  4 toggle public String toString() {
1239  4 return getID();
1240    }
1241   
1242    /**
1243    * By default, when DateTimeZones are serialized, only a "stub" object
1244    * referring to the id is written out. When the stub is read in, it
1245    * replaces itself with a DateTimeZone object.
1246    *
1247    * @return a stub object to go in the stream
1248    */
 
1249  28 toggle protected Object writeReplace() throws ObjectStreamException {
1250  28 return new Stub(iID);
1251    }
1252   
1253    /**
1254    * Used to serialize DateTimeZones by id.
1255    */
 
1256    private static final class Stub implements Serializable {
1257    /**
1258    * Serialization lock.
1259    */
1260    private static final long serialVersionUID = -6471952376487863581L;
1261    /**
1262    * The ID of the zone.
1263    */
1264    private transient String iID;
1265   
1266    /**
1267    * Constructor.
1268    *
1269    * @param id the id of the zone
1270    */
 
1271  28 toggle Stub(String id) {
1272  28 iID = id;
1273    }
1274   
 
1275  28 toggle private void writeObject(ObjectOutputStream out) throws IOException {
1276  28 out.writeUTF(iID);
1277    }
1278   
 
1279  48 toggle private void readObject(ObjectInputStream in) throws IOException {
1280  48 iID = in.readUTF();
1281    }
1282   
 
1283  48 toggle private Object readResolve() throws ObjectStreamException {
1284  48 return forID(iID);
1285    }
1286    }
1287   
1288    //-------------------------------------------------------------------------
1289   
1290    /**
1291    * Lazy initialization to avoid a synchronization lock.
1292    */
 
1293    static final class LazyInit {
1294   
1295    /**
1296    * Cache of old zone IDs to new zone IDs
1297    */
1298    static final Map<String, String> CONVERSION_MAP = buildMap();
1299    /**
1300    * Time zone offset formatter.
1301    */
1302    static final DateTimeFormatter OFFSET_FORMATTER = buildFormatter();
1303   
 
1304  2 toggle private static DateTimeFormatter buildFormatter() {
1305    // Can't use a real chronology if called during class
1306    // initialization. Offset parser doesn't need it anyhow.
1307  2 Chronology chrono = new BaseChronology() {
1308    private static final long serialVersionUID = -3128740902654445468L;
1309   
 
1310  43 toggle public DateTimeZone getZone() {
1311  43 return null;
1312    }
1313   
 
1314  43 toggle public Chronology withUTC() {
1315  43 return this;
1316    }
1317   
 
1318  0 toggle public Chronology withZone(DateTimeZone zone) {
1319  0 return this;
1320    }
1321   
 
1322  0 toggle public String toString() {
1323  0 return getClass().getName();
1324    }
1325    };
1326  2 return new DateTimeFormatterBuilder()
1327    .appendTimeZoneOffset(null, true, 2, 4)
1328    .toFormatter()
1329    .withChronology(chrono);
1330    }
1331   
 
1332  2 toggle private static Map<String, String> buildMap() {
1333    // Backwards compatibility with TimeZone.
1334  2 Map<String, String> map = new HashMap<String, String>();
1335  2 map.put("GMT", "UTC");
1336  2 map.put("WET", "WET");
1337  2 map.put("CET", "CET");
1338  2 map.put("MET", "CET");
1339  2 map.put("ECT", "CET");
1340  2 map.put("EET", "EET");
1341  2 map.put("MIT", "Pacific/Apia");
1342  2 map.put("HST", "Pacific/Honolulu"); // JDK 1.1 compatible
1343  2 map.put("AST", "America/Anchorage");
1344  2 map.put("PST", "America/Los_Angeles");
1345  2 map.put("MST", "America/Denver"); // JDK 1.1 compatible
1346  2 map.put("PNT", "America/Phoenix");
1347  2 map.put("CST", "America/Chicago");
1348  2 map.put("EST", "America/New_York"); // JDK 1.1 compatible
1349  2 map.put("IET", "America/Indiana/Indianapolis");
1350  2 map.put("PRT", "America/Puerto_Rico");
1351  2 map.put("CNT", "America/St_Johns");
1352  2 map.put("AGT", "America/Argentina/Buenos_Aires");
1353  2 map.put("BET", "America/Sao_Paulo");
1354  2 map.put("ART", "Africa/Cairo");
1355  2 map.put("CAT", "Africa/Harare");
1356  2 map.put("EAT", "Africa/Addis_Ababa");
1357  2 map.put("NET", "Asia/Yerevan");
1358  2 map.put("PLT", "Asia/Karachi");
1359  2 map.put("IST", "Asia/Kolkata");
1360  2 map.put("BST", "Asia/Dhaka");
1361  2 map.put("VST", "Asia/Ho_Chi_Minh");
1362  2 map.put("CTT", "Asia/Shanghai");
1363  2 map.put("JST", "Asia/Tokyo");
1364  2 map.put("ACT", "Australia/Darwin");
1365  2 map.put("AET", "Australia/Sydney");
1366  2 map.put("SST", "Pacific/Guadalcanal");
1367  2 map.put("NST", "Pacific/Auckland");
1368  2 return Collections.unmodifiableMap(map);
1369    }
1370    }
1371   
1372    }